#ifndef _CRYPTO_UTIL_H_
#define _CRYPTO_UTIL_H_

#define CRYPTOPP_ENABLE_NAMESPACE_WEAK 1

#include <iostream>
#include <sstream>
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <string>

#include <cryptopp/aes.h>
#include <cryptopp/hex.h>
#include <cryptopp/files.h>
#include <cryptopp/default.h>
#include <cryptopp/filters.h>
#include <cryptopp/osrng.h>
// #include "base64.h"

using namespace CryptoPP;
// extern std::string the_key;
enum AESKeyLength
{
    AES_KEY_LENGTH_16 = 16, AES_KEY_LENGTH_24 = 24, AES_KEY_LENGTH_32 = 32  //密钥的长度
};

class CCryptoUtil
{
public:
    static int encrypt4aes(const std::string &inData, const std::string &strKey,
            std::string &outData, std::string &errMsg)
    {
        outData = "";
        errMsg = "";

        if (inData.empty() || strKey.empty()) // 判断待加密的字符串或者密钥是否为空
        {
            errMsg = "indata or key is empty!!";
            return -1;
        }

        unsigned int iKeyLen = strKey.length();

        if (iKeyLen != AES_KEY_LENGTH_16 && iKeyLen != AES_KEY_LENGTH_24  //判断密钥的长度是否符合要求
                && iKeyLen != AES_KEY_LENGTH_32)
        {
            errMsg = "aes key invalid!!";
            return -2;
        }

        byte iv[AES::BLOCKSIZE];
        int iResult = 0;

        try
        {
            CBC_Mode<AES>::Encryption e;
            e.SetKeyWithIV((byte*) strKey.c_str(), iKeyLen, iv);
            StringSource ss(inData, true, new StreamTransformationFilter(e, new StringSink(outData)));

        } catch (const CryptoPP::Exception& e)
        {
            errMsg = "Encryptor throw exception!!";
            iResult = -3;
        }

        return iResult;
    }

    static int decrypt4aes(const std::string &inData, const std::string &strKey,
            std::string &outData, std::string &errMsg)
    {
        outData = "";
        errMsg = "";

        if (inData.empty() || strKey.empty())
        {
            errMsg = "indata or key is empty!!";
            return -1;
        }

        unsigned int iKeyLen = strKey.length();

        if (iKeyLen != AES_KEY_LENGTH_16 && iKeyLen != AES_KEY_LENGTH_24
                && iKeyLen != AES_KEY_LENGTH_32)
        {
            errMsg = "aes key invalid!!";
            return -2;
        }

        byte iv[AES::BLOCKSIZE];
        int iResult = 0;

        try
        {
            CBC_Mode<AES>::Decryption d;
            d.SetKeyWithIV((byte*) strKey.c_str(), iKeyLen, iv);
            StringSource ss(inData, true,
                    new StreamTransformationFilter(d, new StringSink(outData)));
        }
        catch (const CryptoPP::Exception& e)
        {
            errMsg = "Encryptor throw exception";
            iResult = -3;
        }

        return iResult;
    }


        
    // static std::string aes_encrypt_ecb_base64(std::string data , unsigned char* key, int keylen)
    // {
    //     std::string encrypt_str;
    
    //     try 
    //     {
    //         CryptoPP::ECB_Mode<CryptoPP::AES>::Encryption ecb_encription(key, keylen);
    //         CryptoPP::StreamTransformationFilter stf_encription(
    //             ecb_encription,
    //             new CryptoPP::Base64Encoder(new CryptoPP::StringSink(encrypt_str)),
    //             CryptoPP::BlockPaddingSchemeDef::ZEROS_PADDING
    //         );
    //         stf_encription.Put(reinterpret_cast<const unsigned char*>(data.c_str()), data.length() + 1);
    //         stf_encription.MessageEnd();
    //     }
    //     catch (std::exception e) {
    //         std::cout << e.what() << std::endl;
    //     }
    
    //     return encrypt_str;
    // }

    // static std::string aes_decrypt_ecb_base64(std::string base64_data, unsigned char* key, int keylen)
    // {
    //     try 
    //     {
    //         std::string aes_encrypt_data;
    //         CryptoPP::Base64Decoder decoder;
    //         decoder.Attach(new CryptoPP::StringSink(aes_encrypt_data));
    //         decoder.Put(reinterpret_cast<const unsigned char*>(base64_data.c_str()), base64_data.length());
    //         decoder.MessageEnd();
    
    //         std::string decrypt_data;
    //         CryptoPP::ECB_Mode<CryptoPP::AES>::Decryption ebc_description(key, keylen);
    //         CryptoPP::StreamTransformationFilter stf_description(
    //             ebc_description,
    //             new CryptoPP::StringSink(decrypt_data), 
    //             CryptoPP::BlockPaddingSchemeDef::ZEROS_PADDING
    //         );
    
    //         stf_description.Put(
    //             reinterpret_cast<const unsigned char*>(aes_encrypt_data.c_str()), 
    //             aes_encrypt_data.length()
    //         );
    //         stf_description.MessageEnd();
    
    //         return decrypt_data;
    //     }
    //     catch (std::exception e) {
    //         std::cout << e.what() << std::endl;
    //         return "";
    //     }
    // }


    static std::string aes_encrypt_ecb_hex(std::string data , unsigned char* key, unsigned int keylen = 16)
    {
        std::string encrypt_str;
    
        try 
        {
            CryptoPP::ECB_Mode<CryptoPP::AES>::Encryption ecb_encription(key, keylen);
            CryptoPP::StreamTransformationFilter stf_encription(
                ecb_encription,
                new CryptoPP::HexEncoder(new CryptoPP::StringSink(encrypt_str)),
                CryptoPP::BlockPaddingSchemeDef::ZEROS_PADDING
            );
            stf_encription.Put(reinterpret_cast<const unsigned char*>(data.c_str()), data.length() + 1);
            stf_encription.MessageEnd();
        }
        catch (std::exception e) {
            std::cout << e.what() << std::endl;
        }
    
        return encrypt_str;
    }
    static std::string aes_decrypt_ecb_hex(std::string hex_data, unsigned char* key, unsigned int keylen = 16)
    {
        try
        {
            std::string aes_encrypt_data;
            CryptoPP::HexDecoder decoder;
            decoder.Attach(new CryptoPP::StringSink(aes_encrypt_data));
            decoder.Put(reinterpret_cast<const unsigned char*>(hex_data.c_str()), hex_data.length());
            decoder.MessageEnd();
    
            std::string decrypt_data;
            CryptoPP::ECB_Mode<CryptoPP::AES>::Decryption ebc_description(key, keylen);
            CryptoPP::StreamTransformationFilter stf_description(
                ebc_description,
                new CryptoPP::StringSink(decrypt_data),
                CryptoPP::BlockPaddingSchemeDef::ZEROS_PADDING
            );
    
            stf_description.Put(
                reinterpret_cast<const unsigned char*>(aes_encrypt_data.c_str()),
                aes_encrypt_data.length()
            );
            stf_description.MessageEnd();
    
            return decrypt_data;
        }
        catch (std::exception e) {
            std::cout << e.what() << std::endl;
            return "";
        }
    }

};

    
#endif


